package dsg

import (
	"io"
	"fmt"
)

type DirGraph struct {
	size int
	unuse * LinkSet
	Node * LinkSet
	Edge * LinkMat
	Leaf * LinkSet
	Root * LinkSet
}

func InitDirGraph (size int) * DirGraph {
	var dg DirGraph
	dg.size = size
	dg.Node = InitLinkSet (size)
	dg.Edge = InitLinkMat (size)
	dg.unuse = InitLinkSet (size)
	dg.Leaf = InitLinkSet (size)
	dg.Root = InitLinkSet (size)
	for i := 0; i < size; i ++ {
		dg.unuse.Set (i)
	}
	return &dg
}

func (dg * DirGraph) GetSize () int {
	return dg.size
}

func (dg * DirGraph) Expand (size int) {
	dg.Node.Expand (size)
	dg.Edge.Expand (size)
	dg.unuse.Expand (size)
	dg.Leaf.Expand (size)
	dg.Root.Expand (size)
	for i := dg.size; i < dg.size + size; i ++ {
		dg.unuse.Set (i)
	}
	dg.size += size
}

func (dg * DirGraph) GetUnuseIndex () int {
	return dg.unuse.head
}

func (dg * DirGraph) AddNode (inode int) {
	dg.Node.Set (inode) 
	dg.unuse.UnSet (inode)
	dg.Root.Set (inode)
	dg.Leaf.Set (inode)
}

func (dg * DirGraph) AddEdge (i1, i2 int) {
	if !dg.Node.GetLabel (i1) || !dg.Node.GetLabel (i2) {return}
	dg.Edge.Set (i1, i2)
	dg.Leaf.UnSet (i1)
	dg.Root.UnSet (i2)
}

func (dg * DirGraph) RemoveEdge (i1, i2 int) {
	dg.Edge.UnSet (i1, i2)
	if dg.Edge.GetRowNum (i1) == 0 {
		dg.Leaf.Set(i1)
	}
	if dg.Edge.GetColNum (i2) == 0 {
		dg.Root.Set(i2)
	}
}

func (dg * DirGraph) RemoveNode (inode int) {
      edge := dg.Edge

      for edge.RowTraverseStart(inode); ; edge.RowTraverseForward() {
            _, label := edge.GetRowTraverseLabel()
            if label == -1 {
                  break
            }
            if edge.GetColNum (label) == 1 {
                  dg.Root.Set(label)
            }
      }
      for edge.ColTraverseStart(inode); ; edge.ColTraverseForward() {
            label, _ := edge.GetColTraverseLabel()
            if label == -1 {
                  break
            }
            if edge.GetRowNum (label) == 1 {
                  dg.Leaf.Set(label)
            }
      }

      edge.ClearRow (inode)
      edge.ClearCol (inode)

	dg.Node.UnSet (inode)
	dg.Leaf.UnSet (inode)
	dg.Root.UnSet (inode)
	dg.unuse.Set (inode)
}

func (dg * DirGraph) Save (fout io.Writer) {
	fmt.Fprintf (fout, "%d\n", dg.size)
	dg.Node.Save (fout)
	dg.unuse.Save (fout)
	dg.Edge.Save (fout)
}

func LoadDirGraph (fin io.Reader) * DirGraph {
	var dg DirGraph

	n, _ := fmt.Fscan (fin, &dg.size)
	if n != 1 {return nil}

	dg.Node = LoadLinkSet (fin)
	dg.unuse = LoadLinkSet (fin)
	dg.Edge = LoadLinkMat (fin)
      dg.Leaf = InitLinkSet (dg.size)
      dg.Root = InitLinkSet (dg.size)

	if dg.Edge == nil || dg.unuse == nil || dg.Node == nil {
		return nil
	} 

      node := dg.Node
      for node.TraverseStart(); node.GetTraverseLabel() != -1; node.TraverseForward() {
            label := node.GetTraverseLabel()
            if dg.Edge.GetRowNum (label) == 0 {
                  dg.Leaf.Set (label)
            }
            if dg.Edge.GetColNum (label) == 0 {
                  dg.Root.Set (label)
            }
      }

	return &dg
}

func (dg * DirGraph) GetInEdge (inode int) []int {
      return dg.Edge.GetCol (inode)
}

func (dg * DirGraph) GetOutEdge (inode int) []int {
      return dg.Edge.GetRow (inode)
}

func (dg * DirGraph) GetAllNode () []int {
      return dg.Node.GetAllLabel ()
}

func (dg * DirGraph) GetAllEdge () [][]int {
      return dg.Edge.GetAllLabel ()
}

func (dg * DirGraph) PrintRootLeaf (fout io.Writer) {
      fmt.Fprintf (fout, "Root:\n")
      dg.Root.Save (fout)
      fmt.Fprintf (fout, "Leaf:\n")
      dg.Leaf.Save (fout)
}

